#include <QtCore/QTimer>
#include <QtCore/QDateTime>
#include <QtCore/QStringList>
#include <QtSql/QSqlRecord>
#include "smpp/encoders/smpp_binary_encoder.h"
#include "smpp/decoders/smpp_binary_decoder.h"
#include "smpp/helpers/smpp_help_func.h"
#include "mailer/mailer_p.h"
#include "mailer/mailer.h"
#include "kernel/kernel.h"

namespace smsrouter {

MailerPrivate::MailerPrivate(Mailer *parent) :
  QObject(0),
  parent(parent),
  thread(new QThread(parent)),
  state(STOPED),
  rd_dest_submit_query(0),
  rd_src_deliver_query(0),
  max_write_src_submit(50),
  max_write_src_deliver(50),
  max_write_dest_submit(50),
  max_write_dest_deliver(50),
  max_read_dest_submit(3000),
  max_read_src_deliver(3000),
  proc_timer(-1),
  proc_time(500) {
  parent->moveToThread(thread);
  connect(thread, SIGNAL(started()), this, SLOT(threadStarted()));

  connect(this, SIGNAL(sigStop()), this, SLOT(stop()));
  connect(this, SIGNAL(sigAdd(SrcSubmitSm *)),
          this, SLOT(add(SrcSubmitSm *)));
  connect(this, SIGNAL(sigAdd(DestSubmitSm *)),
          this, SLOT(add(DestSubmitSm *)));
  connect(this, SIGNAL(sigAdd(DestDeliverSm *)),
          this, SLOT(add(DestDeliverSm *)));
  connect(this, SIGNAL(sigAdd(SrcDeliverSm *)),
          this, SLOT(add(SrcDeliverSm *)));
}

MailerPrivate::~MailerPrivate() {
  if(thread) {
    delete thread;
    thread = 0;
  }
}

void MailerPrivate::timerEvent(QTimerEvent *event) {
  int timer_id(event->timerId());

  if(timer_id == proc_timer) {
    writeSrcSubmit();
    writeDestSubmit();
    writeSrcDeliver();
    writeDestDeliver();
    if(state == STARTED) {
      readDestSubmit();
      readSrcDeliver();
    }
  }
}

void MailerPrivate::threadStarted() {
  setState(STARTING);
  setState(STARTED);
}

void MailerPrivate::stop() {
  setState(STOPING);
  checkState();
}

void MailerPrivate::add(SrcSubmitSm *message) {
  wr_src_submit_messages.push_back(message);
  if(wr_src_submit_messages.size() >= max_write_src_submit)
    writeSrcSubmit();
}

void MailerPrivate::add(DestSubmitSm *message) {
  if(rd_dest_submit_messages.removeAll(message) > 0) {
    wr_dest_submit_messages.push_back(message);
    if(wr_dest_submit_messages.size() >= max_write_dest_submit)
      writeDestSubmit();
  } else {
    delete message;
  }
}

void MailerPrivate::add(DestDeliverSm *message) {
  wr_dest_deliver_messages.push_back(message);
  if(wr_dest_deliver_messages.size() >= max_write_dest_deliver)
    writeDestDeliver();
}

void MailerPrivate::add(SrcDeliverSm *message) {
  if(rd_src_deliver_messages.removeAll(message) > 0) {
    wr_src_deliver_messages.push_back(message);
    if(wr_src_deliver_messages.size() >= max_write_src_deliver)
      writeSrcDeliver();
  } else {
    delete message;
  }
}

bool MailerPrivate::isReadDestSubmitDb(quint32 dest_session_id,
                                       quint32 dest_message_id) {
  DestSubmitSm *p(0);

  for(int i = 0; i < rd_dest_submit_messages.size(); i++) {
    p = rd_dest_submit_messages[i];
    if(p->dest_session_id == dest_session_id &&
       p->dest_message_id == dest_message_id) {
      return true;
    }
  }

  for(int i = 0; i < wr_dest_submit_messages.size(); i++) {
    p = wr_dest_submit_messages[i];
    if(p->dest_session_id == dest_session_id &&
       p->dest_message_id == dest_message_id) {
      return true;
    }
  }

  for(QHash<dbmanager::SqlQuery *, QList<DestSubmitSm *> *>::Iterator i =
        query_dest_submit_messages.begin();
      i != query_dest_submit_messages.end();
      i++) {
    QList<DestSubmitSm *> *l = i.value();

    for(int j = 0; j < l->size(); j++) {
      p = l->operator [](j);
      if(p->dest_session_id == dest_session_id &&
         p->dest_message_id == dest_message_id) {
        return true;
      }
    }
  }

  return false;
}

void MailerPrivate::writeSrcSubmit() {
  if(wr_src_submit_messages.isEmpty())
    return;

  QHash<QString, QString> query_messages;

  QString query_directions;

  while(!wr_src_submit_messages.isEmpty()) {
    SrcSubmitSm *message = wr_src_submit_messages.takeFirst();

    if(message->src_session_id == 0) {
      delete message;
      continue;
    }

    QString db_postfix(QDateTime::fromMSecsSinceEpoch(message->receive_date)
                       .toString("dd_MM_yyyy"));

    if(!query_messages.contains(db_postfix)) {
      query_messages.insert(db_postfix, QString());
    }

    QString dest_session_id("NULL");
    if(message->dest_session_id > 0) {
      dest_session_id = QString::number(message->dest_session_id);
    }

    QString receive_date(QString::number(message->receive_date));

    QString final_date("NULL");

    QString next_delivery_date(receive_date);

    bool need_write_directions(
      message->message_state == smpp::parameters::message_state::ENROUTE &&
      message->error_code == smpp::parameters::command_status::ESME_ROK);

    quint8 message_type(0);

    if(!need_write_directions) {
      message_type = 1;
      final_date = receive_date;
      next_delivery_date = "NULL";
    }

    if(need_write_directions) {
      if(query_directions.isEmpty()) {
        query_directions =
          "INSERT INTO `directions` ("
          "`kernel_id`,"
          "`src_session_id`,"
          "`dest_session_id`,"
          "`src_message_id`,"
          "`receive_date`,"
          "`next_delivery_date`,"
          "`service_type`,"
          "`src_addr_ton_before`,"
          "`src_addr_npi_before`,"
          "`src_addr_before`,"
          "`src_addr_ton_after`,"
          "`src_addr_npi_after`,"
          "`src_addr_after`,"
          "`dest_addr_ton_before`,"
          "`dest_addr_npi_before`,"
          "`dest_addr_before`,"
          "`dest_addr_ton_after`,"
          "`dest_addr_npi_after`,"
          "`dest_addr_after`,"
          "`esm_class`,"
          "`protocol_id`,"
          "`priority_flag`,"
          "`schedule_delivery_time`,"
          "`validity_period`,"
          "`registered_delivery`,"
          "`replace_if_present_flag`,"
          "`data_coding`,"
          "`sm_default_msg_id`,"
          "`sm_length`,"
          "`short_message`) VALUES ";
      } else {
        query_directions += ",";
      }

      query_directions += QString(
        "(%1,%2,%3,%4,%5,%6,'%7',%8,%9,'%10',%11,%12,'%13',%14,%15,'%16',"
        "%17,%18,'%19',%20,%21,%22,'%23','%24',%25,%26,%27,%28,%29,'%30')")
          .arg(Kernel::instance()->getKernelId())
          .arg(message->src_session_id)
          .arg(dest_session_id)
          .arg(message->src_message_id)
          .arg(receive_date)
          .arg(next_delivery_date)
          .arg(message->sm.getServiceType())
          .arg(message->sm.getSourceAddrTon())
          .arg(message->sm.getSourceAddrNpi())
          .arg(message->sm.getSourceAddr())
          .arg(message->src_addr_ton_after)
          .arg(message->src_addr_npi_after)
          .arg(message->src_addr_after)
          .arg(message->sm.getDestAddrTon())
          .arg(message->sm.getDestAddrNpi())
          .arg(message->sm.getDestinationAddr())
          .arg(message->sm.getDestAddrTon())
          .arg(message->sm.getDestAddrNpi())
          .arg(message->sm.getDestinationAddr())
          .arg(message->sm.getEsmClass())
          .arg(message->sm.getProtocolId())
          .arg(message->sm.getPriorityFlag())
          .arg(message->sm.getScheduleDeliveryTime())
          .arg(message->sm.getValidityPeriod())
          .arg(message->sm.getRegisteredDelivery())
          .arg(message->sm.getReplaceIfPresentFlag())
          .arg(message->sm.getDataCoding())
          .arg(message->sm.getSmDefaultMsgId())
          .arg(message->sm.getSmLength())
          .arg(QString(message->sm.getShortMessage().toHex()));
    }

    if(query_messages[db_postfix].isEmpty()) {
      query_messages[db_postfix] = QString(
        "INSERT INTO `messages_%1` ("
        "`kernel_id`,"
        "`message_type`,"
        "`src_session_id`,"
        "`dest_session_id`,"
        "`src_message_id`,"
        "`receive_date`,"
        "`next_delivery_date`,"
        "`final_date`,"
        "`service_type`,"
        "`src_addr_ton_before`,"
        "`src_addr_npi_before`,"
        "`src_addr_before`,"
        "`src_addr_ton_after`,"
        "`src_addr_npi_after`,"
        "`src_addr_after`,"
        "`dest_addr_ton_before`,"
        "`dest_addr_npi_before`,"
        "`dest_addr_before`,"
        "`dest_addr_ton_after`,"
        "`dest_addr_npi_after`,"
        "`dest_addr_after`,"
        "`esm_class`,"
        "`protocol_id`,"
        "`priority_flag`,"
        "`schedule_delivery_time`,"
        "`validity_period`,"
        "`registered_delivery`,"
        "`replace_if_present_flag`,"
        "`data_coding`,"
        "`sm_default_msg_id`,"
        "`sm_length`,"
        "`short_message`,"
        "`error_code`,"
        "`message_state`) VALUES ").arg(db_postfix);
    } else {
      query_messages[db_postfix] += ",";
    }

    query_messages[db_postfix] += QString(
      "(%1,%2,%3,%4,%5,%6,%7,%8,'%9',%10,%11,'%12',%13,%14,'%15',%16,%17,"
      "'%18',%19,%20,'%21',%22,%23,%24,'%25','%26',%27,%28,%29,%30,%31,'%32',"
      "%33,%34)")
        .arg(Kernel::instance()->getKernelId())
        .arg(message_type)
        .arg(message->src_session_id)
        .arg(dest_session_id)
        .arg(message->src_message_id)
        .arg(receive_date)
        .arg(next_delivery_date)
        .arg(final_date)
        .arg(message->sm.getServiceType())
        .arg(message->sm.getSourceAddrTon())
        .arg(message->sm.getSourceAddrNpi())
        .arg(message->sm.getSourceAddr())
        .arg(message->src_addr_ton_after)
        .arg(message->src_addr_npi_after)
        .arg(message->src_addr_after)
        .arg(message->sm.getDestAddrTon())
        .arg(message->sm.getDestAddrNpi())
        .arg(message->sm.getDestinationAddr())
        .arg(message->sm.getDestAddrTon())
        .arg(message->sm.getDestAddrNpi())
        .arg(message->sm.getDestinationAddr())
        .arg(message->sm.getEsmClass())
        .arg(message->sm.getProtocolId())
        .arg(message->sm.getPriorityFlag())
        .arg(message->sm.getScheduleDeliveryTime())
        .arg(message->sm.getValidityPeriod())
        .arg(message->sm.getRegisteredDelivery())
        .arg(message->sm.getReplaceIfPresentFlag())
        .arg(message->sm.getDataCoding())
        .arg(message->sm.getSmDefaultMsgId())
        .arg(message->sm.getSmLength())
        .arg(QString(message->sm.getShortMessage().toHex()))
        .arg(message->error_code)
        .arg(message->message_state);

    delete message;
  }

  if(!query_directions.isEmpty()) {
    dbmanager::SqlQuery *query = new dbmanager::SqlQuery(false, true);

    query->add(query_directions);

    connect(query, SIGNAL(executed()),
            this, SLOT(writeSrcSubmitDirectionsExecuted()));

    queries.push_back(query);

    Kernel::instance()->dbDirections()->execute(query);
  }

  for(QHash<QString, QString>::const_iterator i =
        query_messages.constBegin();
      i != query_messages.constEnd();
      i++) {
    dbmanager::SqlQuery *query = new dbmanager::SqlQuery(false, true);

    query->add(i.value());

    connect(query, SIGNAL(executed()),
            this, SLOT(writeSrcSubmitMessagesExecuted()));

    queries.push_back(query);

    Kernel::instance()->dbMessages()->execute(query);
  }
}

void MailerPrivate::writeSrcDeliver() {
  if(wr_src_deliver_messages.isEmpty())
    return;

  QHash<QString, SrcDeliverUpdateMessages *> u_messages;

  SrcDeliverUpdateDirections *u_directions =
     new SrcDeliverUpdateDirections;

  QString d_directions;
  QHash<QString, QString> i_delivery_attempts;

  QString now_date(
    QString::number(QDateTime::currentMSecsSinceEpoch()));

  QString next_delivery_date(now_date);

  QString final_date(now_date);

  quint8 type_message(2);

  while(!wr_src_deliver_messages.isEmpty()) {
    SrcDeliverSm *message = wr_src_deliver_messages.takeFirst();

    QString db_postfix(QDateTime::fromMSecsSinceEpoch(message->receive_date)
                       .toString("dd_MM_yyyy"));

    int timeout(Kernel::instance()->getSrcTimeoutErrorCode(
      message->src_session_id, message->error_code));

    quint8 message_state(smpp::parameters::message_state::ENROUTE);

    SrcSessionSettings *src_session_settings(
      Kernel::instance()->getSrcSessionSettings(message->src_session_id));

    switch(timeout) {
      case -1:
        type_message = 3;
        next_delivery_date = "NULL";
        final_date = now_date;
        message_state = smpp::parameters::message_state::UNDELIVERABLE;
        break;
      case 0:
        type_message = 3;
        next_delivery_date = "NULL";
        final_date = now_date;
        message_state = smpp::parameters::message_state::DELIVERED;
        break;
      default:
        if(message->attempt >= src_session_settings->delivery_attempts) {
          type_message = 3;
          next_delivery_date = "NULL";
          final_date = now_date;
          message_state = smpp::parameters::message_state::UNDELIVERABLE;
        } else {
          type_message = 2;
          next_delivery_date = QString::number(
              QDateTime::currentMSecsSinceEpoch() + timeout * 1000);
          final_date = "NULL";
        }
        break;
    }

    if(!i_delivery_attempts.contains(db_postfix)) {
      i_delivery_attempts.insert(db_postfix, QString());
    }

    if(!u_messages.contains(db_postfix)) {
      u_messages.insert(db_postfix, new SrcDeliverUpdateMessages);
    }

    if(!i_delivery_attempts[db_postfix].isEmpty()) {
      i_delivery_attempts[db_postfix] += ",";
    }

    i_delivery_attempts[db_postfix] += QString("(%1,1,%2,%3,%4)")
      .arg(Kernel::instance()->getKernelId())
      .arg(message->src_message_id)
      .arg(now_date)
      .arg(message->error_code);

    if(!u_messages[db_postfix]->attempt.isEmpty()) {
      u_messages[db_postfix]->type += " ";
      u_messages[db_postfix]->attempt += " ";
      u_messages[db_postfix]->next_delivery_date += " ";
      u_messages[db_postfix]->final_date += " ";
      u_messages[db_postfix]->error_code += " ";
      u_messages[db_postfix]->message_state += " ";
      u_messages[db_postfix]->where += " OR ";
    }

    u_messages[db_postfix]->type += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(type_message);
    u_messages[db_postfix]->attempt += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(message->attempt);
    u_messages[db_postfix]->next_delivery_date += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(next_delivery_date);
    u_messages[db_postfix]->final_date += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(final_date);
    u_messages[db_postfix]->error_code += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(message->error_code);
    u_messages[db_postfix]->message_state += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(message_state);
    u_messages[db_postfix]->where += QString(
      "(`src_message_id`=%1 AND `receive_date`=%2)")
        .arg(message->src_message_id)
        .arg(message->receive_date);

    switch(type_message) {
      case 2: {
          if(!u_directions->attempt.isEmpty()) {
            u_directions->state += " ";
            u_directions->attempt += " ";
            u_directions->next_delivery_date += " ";
            u_directions->error_code += " ";
            u_directions->message_state += " ";
            u_directions->where += ",";
          }

          u_directions->state += QString("WHEN %1 THEN 'R_DLV_DST'")
            .arg(message->src_message_id);
          u_directions->attempt += QString("WHEN %1 THEN %2")
            .arg(message->src_message_id)
            .arg(message->attempt);
          u_directions->next_delivery_date += QString("WHEN %1 THEN %2")
           .arg(message->src_message_id)
           .arg(next_delivery_date);
          u_directions->error_code += QString("WHEN %1 THEN %2")
            .arg(message->src_message_id)
            .arg(message->error_code);
          u_directions->message_state += QString("WHEN %1 THEN %2")
            .arg(message->src_message_id)
            .arg(message_state);
          u_directions->where += QString::number(
            message->src_message_id);
        break;
      }
      default: {
        if(!d_directions.isEmpty()) {
          d_directions += ",";
        }
        d_directions += QString::number(message->src_message_id);
        break;
      }
    }

    delete message;
  }

  dbmanager::SqlQuery *directions_query = new dbmanager::SqlQuery(false, true);
  dbmanager::SqlQuery *messages_query = new dbmanager::SqlQuery(false, true);

  queries.push_back(directions_query);
  queries.push_back(messages_query);

  connect(directions_query, SIGNAL(executed()),
          this, SLOT(writeSrcDeliverDirectionsExecuted()));

  connect(messages_query, SIGNAL(executed()),
          this, SLOT(writeSrcDeliverMessagesExecuted()));

  if(!u_directions->attempt.isEmpty()) {
    directions_query->add(QString(
      "UPDATE `directions` "
      "SET `state` = CASE `src_message_id` %1 END,"
      "`attempt` = CASE `src_message_id` %2 END,"
      "`next_delivery_date` = CASE `src_message_id` %3 END,"
      "`error_code` = CASE `src_message_id` %4 END,"
      "`message_state` = CASE `src_message_id` %5 END "
      "WHERE `state`='T_DLV_SRC' AND `kernel_id`=%6 AND "
      "`src_message_id` IN (%7)")
        .arg(u_directions->state)
        .arg(u_directions->attempt)
        .arg(u_directions->next_delivery_date)
        .arg(u_directions->error_code)
        .arg(u_directions->message_state)
        .arg(Kernel::instance()->getKernelId())
        .arg(u_directions->where));
  }

  if(!d_directions.isEmpty()) {
    directions_query->add(QString(
        "DELETE FROM `directions` "
        "WHERE `state`='T_DLV_SRC' AND `kernel_id`=%1 AND "
        "`src_message_id` IN (%2)")
      .arg(Kernel::instance()->getKernelId())
      .arg(d_directions));
  }

  for(QHash<QString, SrcDeliverUpdateMessages *>::iterator i =
        u_messages.begin();
      i != u_messages.end();
      i++) {
    SrcDeliverUpdateMessages *p(i.value());
    messages_query->add(QString(
      "UPDATE `messages_%1` "
      "SET `message_type` = CASE %2 END,"
      "`attempt` = CASE %3 END,"
      "`next_delivery_date` = CASE %4 END,"
      "`final_date` = CASE %5 END,"
      "`error_code` = CASE %6 END,"
      "`message_state` = CASE %7 END "
      "WHERE `message_type`=2 AND `kernel_id`=%8 AND (%9)")
        .arg(i.key())
        .arg(p->type)
        .arg(p->attempt)
        .arg(p->next_delivery_date)
        .arg(p->final_date)
        .arg(p->error_code)
        .arg(p->message_state)
        .arg(Kernel::instance()->getKernelId())
        .arg(p->where));
    delete p;
  }

  for(QHash<QString, QString>::const_iterator i =
        i_delivery_attempts.constBegin();
      i != i_delivery_attempts.constEnd();
      i++) {
    messages_query->add(QString(
        "INSERT INTO `delivery_attempts_%1` "
        "(`kernel_id`,`message_type`,`src_message_id`,"
        "`deliver_date`,`error_code`) VALUES %2")
      .arg(i.key())
      .arg(i.value()));
  }

  delete u_directions;

  Kernel::instance()->dbDirections()->execute(directions_query);
  Kernel::instance()->dbMessages()->execute(messages_query);
}

void MailerPrivate::writeDestSubmit() {
  if(wr_dest_submit_messages.isEmpty())
    return;

  QHash<QString, DestSubmitUpdateMessages *> u_messages;

  DestSubmitUpdateDirections *u_directions =
     new DestSubmitUpdateDirections;

  QHash<QString, QString> i_delivery_attempts;
  QHash<QString, QString> i_messages;

  QList<DestSubmitSm *> *query_dest_submit(new QList<DestSubmitSm *>());

  QString now_date(
    QString::number(QDateTime::currentMSecsSinceEpoch()));

  QString next_delivery_date_d(now_date);

  QString next_delivery_date_m(now_date);

  QString final_date(now_date);

  quint8 type_message(0);

  while(!wr_dest_submit_messages.isEmpty()) {
    DestSubmitSm *message = wr_dest_submit_messages.takeFirst();

    QString db_postfix(QDateTime::fromMSecsSinceEpoch(message->receive_date)
                       .toString("dd_MM_yyyy"));

    DestSessionSettings *dest_sessions_settings(
      Kernel::instance()->getDestSessionSettings(message->dest_session_id));

    query_dest_submit->push_back(message);

    int timeout(Kernel::instance()->getDestTimeoutErrorCode(
      message->dest_session_id, message->error_code));

    quint8 state(0);
    quint8 message_state(smpp::parameters::message_state::ENROUTE);

    if(!i_delivery_attempts.contains(db_postfix)) {
      i_delivery_attempts.insert(db_postfix, QString());
    }

    if(!u_messages.contains(db_postfix)) {
      u_messages.insert(db_postfix, new DestSubmitUpdateMessages);
    }

    switch(timeout) {
      case -1:
        type_message = 1;
        state = 2;
        next_delivery_date_m = "NULL";
        next_delivery_date_d = now_date;
        final_date = now_date;
        message_state = smpp::parameters::message_state::UNDELIVERABLE;
        break;
      case 0:
        type_message = 1;
        next_delivery_date_m = "NULL";
        next_delivery_date_d = "NULL";
        final_date = now_date;
        message_state = smpp::parameters::message_state::DELIVERED;
        break;
      default:
        if(message->attempt >= dest_sessions_settings->delivery_attempts) {
          type_message = 1;
          state = 2;
          next_delivery_date_m = "NULL";
          next_delivery_date_d = now_date;
          final_date = now_date;
          message_state = smpp::parameters::message_state::UNDELIVERABLE;
        } else {
          type_message = 0;
          state = 1;
          next_delivery_date_m = QString::number(
            QDateTime::currentMSecsSinceEpoch() + timeout * 1000);
          next_delivery_date_d = next_delivery_date_m;
          final_date = "NULL";
        }
        break;
    }

    if(!i_delivery_attempts[db_postfix].isEmpty()) {
      i_delivery_attempts[db_postfix] += ",";
    }

    i_delivery_attempts[db_postfix] += QString("(%1,0,%2,%3,%4)")
      .arg(Kernel::instance()->getKernelId())
      .arg(message->src_message_id)
      .arg(now_date)
      .arg(message->error_code);

    if(!u_messages[db_postfix]->attempt.isEmpty()) {
      u_messages[db_postfix]->type += " ";
      u_messages[db_postfix]->dest_message_id += " ";
      u_messages[db_postfix]->attempt += " ";
      u_messages[db_postfix]->next_delivery_date += " ";
      u_messages[db_postfix]->final_date += " ";
      u_messages[db_postfix]->error_code += " ";
      u_messages[db_postfix]->message_state += " ";
      u_messages[db_postfix]->where += " OR ";
    }

    u_messages[db_postfix]->type += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(type_message);
    u_messages[db_postfix]->dest_message_id += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(message->dest_message_id);
    u_messages[db_postfix]->attempt += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(message->attempt);
    u_messages[db_postfix]->next_delivery_date += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(next_delivery_date_m);
    u_messages[db_postfix]->final_date += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(final_date);
    u_messages[db_postfix]->error_code += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(message->error_code);
    u_messages[db_postfix]->message_state += QString(
      "WHEN (`src_message_id`=%1 AND `receive_date`=%2) THEN %3")
        .arg(message->src_message_id)
        .arg(message->receive_date)
        .arg(message_state);
    u_messages[db_postfix]->where += QString(
      "(`src_message_id`=%1 AND `receive_date`=%2)")
        .arg(message->src_message_id)
        .arg(message->receive_date);

    switch(state) {
      case 0: {
        if(!u_directions->attempt.isEmpty()) {
          u_directions->state += " ";
          u_directions->dest_message_id += " ";
          u_directions->attempt += " ";
          u_directions->receive_date += " ";
          u_directions->next_delivery_date += " ";
          u_directions->error_code += " ";
          u_directions->message_state += " ";
          u_directions->src_addr_ton_before += " ";
          u_directions->src_addr_npi_before += " ";
          u_directions->src_addr_before += " ";
          u_directions->src_addr_ton_after += " ";
          u_directions->src_addr_npi_after += " ";
          u_directions->src_addr_after += " ";
          u_directions->dest_addr_ton_before += " ";
          u_directions->dest_addr_npi_before += " ";
          u_directions->dest_addr_before += " ";
          u_directions->dest_addr_ton_after += " ";
          u_directions->dest_addr_npi_after += " ";
          u_directions->dest_addr_after += " ";
          u_directions->sm_length += " ";
          u_directions->short_message += " ";
          u_directions->where += ",";
        }

        u_directions->state += QString("WHEN %1 THEN 'R_SBT_RESP_DST'")
          .arg(message->src_message_id);
        u_directions->dest_message_id += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->dest_message_id);
        u_directions->attempt += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->attempt);
        u_directions->receive_date += QString("WHEN %1 THEN %2")
         .arg(message->src_message_id)
         .arg(message->receive_date);
        u_directions->next_delivery_date += QString("WHEN %1 THEN %2")
         .arg(message->src_message_id)
         .arg(next_delivery_date_d);
        u_directions->error_code += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->error_code);
        u_directions->message_state += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message_state);
        u_directions->src_addr_ton_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->src_addr_ton_before);
        u_directions->src_addr_npi_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->src_addr_npi_before);
        u_directions->src_addr_before += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->src_addr_before);
        u_directions->src_addr_ton_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getSourceAddrTon());
        u_directions->src_addr_npi_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getSourceAddrNpi());
        u_directions->src_addr_after += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->sm.getSourceAddr());
        u_directions->dest_addr_ton_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrTon());
        u_directions->dest_addr_npi_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrNpi());
        u_directions->dest_addr_before += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->sm.getDestinationAddr());
        u_directions->dest_addr_ton_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrTon());
        u_directions->dest_addr_npi_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrNpi());
        u_directions->dest_addr_after += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->sm.getDestinationAddr());
        u_directions->sm_length += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getSmLength());
        u_directions->short_message += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(QString(message->sm.getShortMessage().toHex()));
        u_directions->where += QString::number(
          message->src_message_id);
        break;
      }
      case 1: {
        if(!u_directions->attempt.isEmpty()) {
          u_directions->state += " ";
          u_directions->dest_message_id += " ";
          u_directions->attempt += " ";
          u_directions->receive_date += " ";
          u_directions->next_delivery_date += " ";
          u_directions->error_code += " ";
          u_directions->message_state += " ";
          u_directions->src_addr_ton_before += " ";
          u_directions->src_addr_npi_before += " ";
          u_directions->src_addr_before += " ";
          u_directions->src_addr_ton_after += " ";
          u_directions->src_addr_npi_after += " ";
          u_directions->src_addr_after += " ";
          u_directions->dest_addr_ton_before += " ";
          u_directions->dest_addr_npi_before += " ";
          u_directions->dest_addr_before += " ";
          u_directions->dest_addr_ton_after += " ";
          u_directions->dest_addr_npi_after += " ";
          u_directions->dest_addr_after += " ";
          u_directions->sm_length += " ";
          u_directions->short_message += " ";
          u_directions->where += ",";
        }

        u_directions->state += QString("WHEN %1 THEN 'R_SBT_SRC'")
          .arg(message->src_message_id);
        u_directions->dest_message_id += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->dest_message_id);
        u_directions->attempt += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->attempt);
        u_directions->receive_date += QString("WHEN %1 THEN %2")
         .arg(message->src_message_id)
         .arg(message->receive_date);
        u_directions->next_delivery_date += QString("WHEN %1 THEN %2")
         .arg(message->src_message_id)
         .arg(next_delivery_date_d);
        u_directions->error_code += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->error_code);
        u_directions->message_state += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message_state);
        u_directions->src_addr_ton_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->src_addr_ton_before);
        u_directions->src_addr_npi_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->src_addr_npi_before);
        u_directions->src_addr_before += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->src_addr_before);
        u_directions->src_addr_ton_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getSourceAddrTon());
        u_directions->src_addr_npi_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getSourceAddrNpi());
        u_directions->src_addr_after += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->sm.getSourceAddr());
        u_directions->dest_addr_ton_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrTon());
        u_directions->dest_addr_npi_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrNpi());
        u_directions->dest_addr_before += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->sm.getDestinationAddr());
        u_directions->dest_addr_ton_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrTon());
        u_directions->dest_addr_npi_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrNpi());
        u_directions->dest_addr_after += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->sm.getDestinationAddr());
        u_directions->sm_length += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getSmLength());
        u_directions->short_message += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(QString(message->sm.getShortMessage().toHex()));
        u_directions->where += QString::number(
          message->src_message_id);
        break;
      }
      default: {
        QByteArray submit_text;

        if((message->sm.getEsmClass() & 0x40) == 0) {
          submit_text = message->sm.getShortMessage().mid(0, 20);
        } else {
          QByteArray short_message(message->sm.getShortMessage());
          if(short_message.size() > 7) {
            submit_text = short_message.mid(6, 20);
          }
        }

        QString stat;

        switch(message_state) {
          case smpp::parameters::message_state::ACCEPTED:
            stat = "ACCEPTD";
            break;
          case smpp::parameters::message_state::DELIVERED:
            stat = "DELIVRD";
            break;
          case smpp::parameters::message_state::EXPIRED:
            stat = "EXPIRED";
            break;
          case smpp::parameters::message_state::DELETED:
            stat = "DELETED";
            break;
          case smpp::parameters::message_state::UNDELIVERABLE:
            stat = "UNDELIV";
            break;
          case smpp::parameters::message_state::UNKNOWN:
            stat = "UNKNOWN";
            break;
          case smpp::parameters::message_state::REJECTED:
            stat = "REJECTD";
            break;
          default:
            stat = "UNKNOWN";
            break;
        }

        QByteArray deliver_text = smpp::helpers::toDataCodes(
          "id:" + QString().sprintf("%.10d",
                                    message->src_message_id).toUtf8() +
          " sub:001 dlvrd:001 submit date:" +
          QDateTime::fromMSecsSinceEpoch(message->receive_date)
            .toString("yyMMddHHmm").toUtf8() +
          " done date:" + QDateTime::currentDateTime()
            .toString("yyMMddHHmm").toUtf8() +
          " stat:" + stat.toUtf8() +
          " err:" + QString().sprintf("%03x", message->error_code).toUtf8() +
          " text:" + submit_text, message->sm.getDataCoding());

        if(!u_directions->attempt.isEmpty()) {
          u_directions->state += " ";
          u_directions->dest_message_id += " ";
          u_directions->attempt += " ";
          u_directions->receive_date += " ";
          u_directions->next_delivery_date += " ";
          u_directions->error_code += " ";
          u_directions->message_state += " ";
          u_directions->src_addr_ton_before += " ";
          u_directions->src_addr_npi_before += " ";
          u_directions->src_addr_before += " ";
          u_directions->src_addr_ton_after += " ";
          u_directions->src_addr_npi_after += " ";
          u_directions->src_addr_after += " ";
          u_directions->dest_addr_ton_before += " ";
          u_directions->dest_addr_npi_before += " ";
          u_directions->dest_addr_before += " ";
          u_directions->dest_addr_ton_after += " ";
          u_directions->dest_addr_npi_after += " ";
          u_directions->dest_addr_after += " ";
          u_directions->sm_length += " ";
          u_directions->short_message += " ";
          u_directions->where += ",";
        }

        u_directions->state += QString("WHEN %1 THEN 'R_DLV_DST'")
          .arg(message->src_message_id);
        u_directions->dest_message_id += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->dest_message_id);
        u_directions->attempt += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->attempt);
        u_directions->receive_date += QString("WHEN %1 THEN %2")
         .arg(message->src_message_id)
         .arg(now_date);
        u_directions->next_delivery_date += QString("WHEN %1 THEN %2")
         .arg(message->src_message_id)
         .arg(next_delivery_date_d);
        u_directions->error_code += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->error_code);
        u_directions->message_state += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message_state);
        u_directions->src_addr_ton_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrTon());
        u_directions->src_addr_npi_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrNpi());
        u_directions->src_addr_before += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->sm.getDestinationAddr());
        u_directions->src_addr_ton_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrTon());
        u_directions->src_addr_npi_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getDestAddrNpi());
        u_directions->src_addr_after += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->sm.getDestinationAddr());
        u_directions->dest_addr_ton_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getSourceAddrTon());
        u_directions->dest_addr_npi_before += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->sm.getSourceAddrNpi());
        u_directions->dest_addr_before += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->sm.getSourceAddr());
        u_directions->dest_addr_ton_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->src_addr_ton_before);
        u_directions->dest_addr_npi_after += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(message->src_addr_npi_before);
        u_directions->dest_addr_after += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(message->src_addr_before);
        u_directions->sm_length += QString("WHEN %1 THEN %2")
          .arg(message->src_message_id)
          .arg(deliver_text.size());
        u_directions->short_message += QString("WHEN %1 THEN '%2'")
          .arg(message->src_message_id)
          .arg(QString(deliver_text.toHex()));
        u_directions->where += QString::number(
          message->src_message_id);

        db_postfix = QDateTime::fromMSecsSinceEpoch(now_date.toULongLong())
                     .toString("dd_MM_yyyy");

        if(!i_messages.contains(db_postfix)) {
          i_messages.insert(db_postfix, QString());
        }

        if(!i_messages[db_postfix].isEmpty()) {
          i_messages[db_postfix] += ",";
        }

        i_messages[db_postfix] += QString(
          "(%1,2,%2,%3,%4,%5,%6,%7,'%8',%9,%10,'%11',%12,%13,'%14',%15,%16,"
          "'%17',%18,%19,'%20',%21,%22,%23,'%24','%25',%26,%27,%28,%29,%30,"
          "'%31',%32,%33)")
          .arg(Kernel::instance()->getKernelId())
          .arg(message->src_session_id)
          .arg(message->dest_session_id)
          .arg(message->src_message_id)
          .arg(message->dest_message_id)
          .arg(now_date)
          .arg(now_date)
          .arg("")
          .arg(message->sm.getDestAddrTon())
          .arg(message->sm.getDestAddrNpi())
          .arg(message->sm.getDestinationAddr())
          .arg(message->sm.getDestAddrTon())
          .arg(message->sm.getDestAddrNpi())
          .arg(message->sm.getDestinationAddr())
          .arg(message->sm.getSourceAddrTon())
          .arg(message->sm.getSourceAddrNpi())
          .arg(message->sm.getSourceAddr())
          .arg(message->src_addr_ton_before)
          .arg(message->src_addr_npi_before)
          .arg(message->src_addr_before)
          .arg(0x04)
          .arg(0)
          .arg(0)
          .arg("")
          .arg("")
          .arg(0)
          .arg(0)
          .arg(0)
          .arg(0)
          .arg(deliver_text.size())
          .arg(QString(deliver_text.toHex()))
          .arg(message->error_code)
          .arg(message_state);
        break;
      }
    }
  }

  dbmanager::SqlQuery *directions_query = new dbmanager::SqlQuery(false, true);
  dbmanager::SqlQuery *messages_query = new dbmanager::SqlQuery(false, true);

  queries.push_back(directions_query);
  query_dest_submit_messages.insert(directions_query, query_dest_submit);

  connect(directions_query, SIGNAL(executed()),
          this, SLOT(writeDestSubmitDirectionsExecuted()));

  connect(messages_query, SIGNAL(executed()),
          this, SLOT(writeDestSubmitMessagesExecuted()));

  queries.push_back(messages_query);

  if(!u_directions->attempt.isEmpty()) {
    directions_query->add(QString(
      "UPDATE `directions` "
      "SET `state` = CASE `src_message_id` %1 END,"
      "`dest_message_id` = CASE `src_message_id` %2 END,"
      "`attempt` = CASE `src_message_id` %3 END,"
      "`receive_date` = CASE `src_message_id` %4 END,"
      "`next_delivery_date` = CASE `src_message_id` %5 END,"
      "`error_code` = CASE `src_message_id` %6 END,"
      "`message_state` = CASE `src_message_id` %7 END,"
      "`src_addr_ton_before` = CASE `src_message_id` %8 END,"
      "`src_addr_npi_before` = CASE `src_message_id` %9 END,"
      "`src_addr_before` = CASE `src_message_id` %10 END,"
      "`src_addr_ton_after` = CASE `src_message_id` %11 END,"
      "`src_addr_npi_after` = CASE `src_message_id` %12 END,"
      "`src_addr_after` = CASE `src_message_id` %13 END,"
      "`dest_addr_ton_before` = CASE `src_message_id` %14 END,"
      "`dest_addr_npi_before` = CASE `src_message_id` %15 END,"
      "`dest_addr_before` = CASE `src_message_id` %16 END,"
      "`dest_addr_ton_after` = CASE `src_message_id` %17 END,"
      "`dest_addr_npi_after` = CASE `src_message_id` %18 END,"
      "`dest_addr_after` = CASE `src_message_id` %19 END,"
      "`sm_length` = CASE `src_message_id` %20 END,"
      "`short_message` = CASE `src_message_id` %21 END "
      "WHERE `state`='T_SBT_DST' AND `kernel_id`=%22 AND "
      "`src_message_id` IN (%23)")
        .arg(u_directions->state)
        .arg(u_directions->dest_message_id)
        .arg(u_directions->attempt)
        .arg(u_directions->receive_date)
        .arg(u_directions->next_delivery_date)
        .arg(u_directions->error_code)
        .arg(u_directions->message_state)
        .arg(u_directions->src_addr_ton_before)
        .arg(u_directions->src_addr_npi_before)
        .arg(u_directions->src_addr_before)
        .arg(u_directions->src_addr_ton_after)
        .arg(u_directions->src_addr_npi_after)
        .arg(u_directions->src_addr_after)
        .arg(u_directions->dest_addr_ton_before)
        .arg(u_directions->dest_addr_npi_before)
        .arg(u_directions->dest_addr_before)
        .arg(u_directions->dest_addr_ton_after)
        .arg(u_directions->dest_addr_npi_after)
        .arg(u_directions->dest_addr_after)
        .arg(u_directions->sm_length)
        .arg(u_directions->short_message)
        .arg(Kernel::instance()->getKernelId())
        .arg(u_directions->where));
  }

  for(QHash<QString, DestSubmitUpdateMessages *>::iterator i =
        u_messages.begin();
      i != u_messages.end();
      i++) {
    DestSubmitUpdateMessages *p(i.value());

    messages_query->add(QString(
      "UPDATE `messages_%1` "
      "SET `message_type` = CASE %2 END,"
      "`dest_message_id` = CASE %3 END,"
      "`attempt` = CASE %4 END,"
      "`next_delivery_date` = CASE %5 END,"
      "`final_date` = CASE %6 END,"
      "`error_code` = CASE %7 END,"
      "`message_state` = CASE %8 END "
      "WHERE `message_type`=0 AND `kernel_id`=%9 AND (%10)")
        .arg(i.key())
        .arg(p->type)
        .arg(p->dest_message_id)
        .arg(p->attempt)
        .arg(p->next_delivery_date)
        .arg(p->final_date)
        .arg(p->error_code)
        .arg(p->message_state)
        .arg(Kernel::instance()->getKernelId())
        .arg(p->where));
    delete p;
  }

  for(QHash<QString, QString>::const_iterator i =
        i_delivery_attempts.constBegin();
      i != i_delivery_attempts.constEnd();
      i++) {
    messages_query->add(QString(
        "INSERT INTO `delivery_attempts_%1` "
        "(`kernel_id`,`message_type`,`src_message_id`,"
        "`deliver_date`,`error_code`) VALUES %2")
      .arg(i.key())
      .arg(i.value()));
  }

  for(QHash<QString, QString>::const_iterator i =
        i_messages.constBegin();
      i != i_messages.constEnd();
      i++) {
    messages_query->add(QString(
      "INSERT INTO `messages_%1` ("
      "`kernel_id`,"
      "`message_type`,"
      "`src_session_id`,"
      "`dest_session_id`,"
      "`src_message_id`,"
      "`dest_message_id`,"
      "`receive_date`,"
      "`next_delivery_date`,"
      "`service_type`,"
      "`src_addr_ton_before`,"
      "`src_addr_npi_before`,"
      "`src_addr_before`,"
      "`src_addr_ton_after`,"
      "`src_addr_npi_after`,"
      "`src_addr_after`,"
      "`dest_addr_ton_before`,"
      "`dest_addr_npi_before`,"
      "`dest_addr_before`,"
      "`dest_addr_ton_after`,"
      "`dest_addr_npi_after`,"
      "`dest_addr_after`,"
      "`esm_class`,"
      "`protocol_id`,"
      "`priority_flag`,"
      "`schedule_delivery_time`,"
      "`validity_period`,"
      "`registered_delivery`,"
      "`replace_if_present_flag`,"
      "`data_coding`,"
      "`sm_default_msg_id`,"
      "`sm_length`,"
      "`short_message`,"
      "`error_code_smsc`,"
      "`message_state_smsc`) VALUES %2")
      .arg(i.key())
      .arg(i.value()));
  }

  delete u_directions;

  Kernel::instance()->dbDirections()->execute(directions_query);
  Kernel::instance()->dbMessages()->execute(messages_query);
}

void MailerPrivate::writeDestDeliver() {
  if(wr_dest_deliver_messages.isEmpty())
    return;

  QString s_directions;

  QList<DestDeliverSm *> *dest_deliver_messages =
    new QList<DestDeliverSm *>();

  for(int i = 0; i < wr_dest_deliver_messages.size();) {
    DestDeliverSm *message = wr_dest_deliver_messages[i];

    if(isReadDestSubmitDb(message->dest_session_id, message->dest_message_id)) {
      i++;
      continue;
    }

    if(!s_directions.isEmpty()) {
      s_directions += " OR ";
    }

    s_directions += QString(
        "(`dest_session_id`=%1 AND `dest_message_id`=%2)")
      .arg(message->dest_session_id)
      .arg(message->dest_message_id);

    wr_dest_deliver_messages.removeAt(i);

    dest_deliver_messages->push_back(message);
  }

  if(dest_deliver_messages->isEmpty()) {
    delete dest_deliver_messages;
    return;
  }

  dbmanager::SqlQuery *query = new dbmanager::SqlQuery;

  connect(query, SIGNAL(executed()), this, SLOT(selectDestDeliverExecuted()));

  query_dest_deliver_messages.insert(query, dest_deliver_messages);
  queries.push_back(query);

  query->add(QString(
      "SELECT `src_session_id`,"
      "`src_message_id`,"
      "`dest_session_id`,"
      "`dest_message_id`,"
      "`receive_date`,"
      "`service_type`,"
      "`src_addr_ton_before`,"
      "`src_addr_npi_before`,"
      "`src_addr_before`,"
      "`src_addr_ton_after`,"
      "`src_addr_npi_after`,"
      "`src_addr_after`,"
      "`dest_addr_ton_before`,"
      "`dest_addr_npi_before`,"
      "`dest_addr_before`,"
      "`dest_addr_ton_after`,"
      "`dest_addr_npi_after`,"
      "`dest_addr_after`,"
      "`esm_class`,"
      "`protocol_id`,"
      "`priority_flag`,"
      "`schedule_delivery_time`,"
      "`validity_period`,"
      "`registered_delivery`,"
      "`replace_if_present_flag`,"
      "`data_coding`,"
      "`sm_default_msg_id`,"
      "`short_message` "
      "FROM `directions` "
      "WHERE `kernel_id`=%1 AND `state`='R_SBT_RESP_DST' AND (%2)")
    .arg(Kernel::instance()->getKernelId())
    .arg(s_directions));

  Kernel::instance()->dbDirections()->execute(query);
}

void MailerPrivate::readDestSubmit() {
  if(rd_dest_submit_query) {
    return;
  }

  dbmanager::SqlQuery *query = new dbmanager::SqlQuery;

  query->add(QString("SELECT `src_session_id`,"
                     "`dest_session_id`,"
                     "`receive_date`,"
                     "`src_message_id`,"
                     "`attempt`,"
                     "`service_type`,"
                     "`src_addr_ton_after`,"
                     "`src_addr_npi_after`,"
                     "`src_addr_after`,"
                     "`dest_addr_ton_after`,"
                     "`dest_addr_npi_after`,"
                     "`dest_addr_after`,"
                     "`esm_class`,"
                     "`protocol_id`,"
                     "`priority_flag`,"
                     "`schedule_delivery_time`,"
                     "`validity_period`,"
                     "`registered_delivery`,"
                     "`replace_if_present_flag`,"
                     "`data_coding`,"
                     "`sm_default_msg_id`,"
                     "`sm_length`,"
                     "`short_message`,"
                     "`src_addr_ton_before`,"
                     "`src_addr_npi_before`,"
                     "`src_addr_before` "
                     "FROM `directions` "
                     "WHERE `state`='R_SBT_SRC' AND "
                     "`next_delivery_date`<=%1 AND "
                     "`kernel_id`=%2 LIMIT %3")
               .arg(QDateTime::currentMSecsSinceEpoch())
               .arg(Kernel::instance()->getKernelId())
               .arg(max_read_dest_submit));

  connect(query, SIGNAL(executed()), this, SLOT(readDestSubmitExecuted()));

  queries.push_back(query);
  rd_dest_submit_query = query;

  Kernel::instance()->dbDirections()->execute(query);
}

void MailerPrivate::readSrcDeliver() {
  if(rd_src_deliver_query) {
    return;
  }

  dbmanager::SqlQuery *query = new dbmanager::SqlQuery;

  query->add(QString(
    "SELECT `src_session_id`,"
    "`receive_date`,"
    "`src_message_id`,"
    "`attempt`,"
    "`service_type`,"
    "`src_addr_ton_after`,"
    "`src_addr_npi_after`,"
    "`src_addr_after`,"
    "`dest_addr_ton_after`,"
    "`dest_addr_npi_after`,"
    "`dest_addr_after`,"
    "`esm_class`,"
    "`protocol_id`,"
    "`priority_flag`,"
    "`schedule_delivery_time`,"
    "`validity_period`,"
    "`registered_delivery`,"
    "`replace_if_present_flag`,"
    "`data_coding`,"
    "`sm_default_msg_id`,"
    "`sm_length`,"
    "`short_message`,"
    "`src_addr_ton_before`,"
    "`src_addr_npi_before`,"
    "`src_addr_before` "
    "FROM `directions` "
    "WHERE `state`='R_DLV_DST' AND "
    "`next_delivery_date`<=%1 AND "
    "`kernel_id`=%2 LIMIT %3")
      .arg(QDateTime::currentMSecsSinceEpoch())
      .arg(Kernel::instance()->getKernelId())
      .arg(max_read_src_deliver));

  connect(query, SIGNAL(executed()), this, SLOT(readSrcDeliverExecuted()));

  queries.push_back(query);
  rd_src_deliver_query = query;

  Kernel::instance()->dbDirections()->execute(query);
}

void MailerPrivate::writeSrcSubmitDirectionsExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);
  if(!q->success() && Kernel::instance()->getLogLevel() >= smslog::ERROR) {
    QMultiHash<int, QVariant> params;
    params.insert(logparam::ACTION_P, QString("writeSrcSubmitDirections"));
    params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
    params.insert(logparam::ERROR_MSG_P, q->getFaultString());

    LogEncoder::instance()->encode(smslog::ERROR,
                                   logclass::MAILER,
                                   logclass::MAILER,
                                   logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                   params);
  }
  delete q;
  checkState();
}

void MailerPrivate::writeSrcSubmitMessagesExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);
  if(!q->success() && Kernel::instance()->getLogLevel() >= smslog::WARNING) {
    QMultiHash<int, QVariant> params;
    params.insert(logparam::ACTION_P, QString("writeSrcSubmitMessages"));
    params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
    params.insert(logparam::ERROR_MSG_P, q->getFaultString());

    LogEncoder::instance()->encode(smslog::WARNING,
                                   logclass::MAILER,
                                   logclass::MAILER,
                                   logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                   params);
  }
  delete q;
  checkState();
}

void MailerPrivate::writeSrcDeliverDirectionsExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);
  if(!q->success() && Kernel::instance()->getLogLevel() >= smslog::WARNING) {
    QMultiHash<int, QVariant> params;
    params.insert(logparam::ACTION_P, QString("writeSrcDeliverDirections"));
    params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
    params.insert(logparam::ERROR_MSG_P, q->getFaultString());

    LogEncoder::instance()->encode(smslog::WARNING,
                                   logclass::MAILER,
                                   logclass::MAILER,
                                   logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                   params);
  }
  delete q;
  checkState();
}

void MailerPrivate::writeSrcDeliverMessagesExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);
  if(!q->success() && Kernel::instance()->getLogLevel() >= smslog::WARNING) {
    QMultiHash<int, QVariant> params;
    params.insert(logparam::ACTION_P, QString("writeSrcDeliverMessages"));
    params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
    params.insert(logparam::ERROR_MSG_P, q->getFaultString());

    LogEncoder::instance()->encode(smslog::WARNING,
                                   logclass::MAILER,
                                   logclass::MAILER,
                                   logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                   params);
  }
  delete q;
  checkState();
}

void MailerPrivate::writeDestSubmitDirectionsExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);

  QList<DestSubmitSm *> *l = query_dest_submit_messages.take(q);
  if(l) {
    qDeleteAll(l->begin(), l->end());
    l->clear();
    delete l;
    l = 0;
  }

  if(!q->success() && Kernel::instance()->getLogLevel() >= smslog::WARNING) {
    QMultiHash<int, QVariant> params;
    params.insert(logparam::ACTION_P, QString("writeDestSubmitDirections"));
    params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
    params.insert(logparam::ERROR_MSG_P, q->getFaultString());

    LogEncoder::instance()->encode(smslog::WARNING,
                                   logclass::MAILER,
                                   logclass::MAILER,
                                   logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                   params);
  }

  delete q;
  checkState();
}

void MailerPrivate::writeDestSubmitMessagesExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);

  if(!q->success() && Kernel::instance()->getLogLevel() >= smslog::WARNING) {
    QMultiHash<int, QVariant> params;
    params.insert(logparam::ACTION_P, QString("writeDestSubmitMessages"));
    params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
    params.insert(logparam::ERROR_MSG_P, q->getFaultString());

    LogEncoder::instance()->encode(smslog::WARNING,
                                   logclass::MAILER,
                                   logclass::MAILER,
                                   logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                   params);
  }

  delete q;
  checkState();
}

void MailerPrivate::selectDestDeliverExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);

  QList<DestDeliverSm *> *l = query_dest_deliver_messages.take(q);
  DestDeliverUpdateDirections *u_directions = new DestDeliverUpdateDirections;
  QHash<QString, QString> i_messages;
  QString d_directions;

  if(q->success()) {
    QList<QSqlRecord *> res(q->results());

    quint64 now_date(QDateTime::currentMSecsSinceEpoch());

    while(!res.isEmpty()) {
      QSqlRecord *r(res.takeFirst());
      quint32 src_session_id(r->value(0).toUInt());
      quint32 src_message_id(r->value(1).toUInt());
      quint32 dest_session_id(r->value(2).toUInt());
      quint32 dest_message_id(r->value(3).toUInt());
      quint64 receive_date(r->value(4).toULongLong());
      QString service_type(r->value(5).toString());
      quint8 src_addr_ton_before(r->value(6).toUInt());
      quint8 src_addr_npi_before(r->value(7).toUInt());
      QString src_addr_before(r->value(8).toString());
      quint8 src_addr_ton_after(r->value(9).toUInt());
      quint8 src_addr_npi_after(r->value(10).toUInt());
      QString src_addr_after(r->value(11).toString());
      quint8 dest_addr_ton_before(r->value(12).toUInt());
      quint8 dest_addr_npi_before(r->value(13).toUInt());
      QString dest_addr_before(r->value(14).toString());
      quint8 dest_addr_ton_after(r->value(15).toUInt());
      quint8 dest_addr_npi_after(r->value(16).toUInt());
      QString dest_addr_after(r->value(17).toString());
      quint8 esm_class(r->value(18).toUInt());
      quint8 protocol_id(r->value(19).toUInt());
      quint8 priority_flag(r->value(20).toUInt());
      QString schedule_delivery_time(r->value(21).toString());
      QString validity_period(r->value(22).toString());
      quint8 registered_delivery(r->value(23).toUInt());
      quint8 replace_if_present_flag(r->value(24).toUInt());
      quint8 data_coding(r->value(25).toUInt());
      quint8 sm_default_msg_id(r->value(26).toUInt());
      QByteArray short_message(
        QByteArray::fromHex(r->value(27).toByteArray()));

      for(int i = 0; i < l->size();) {
        DestDeliverSm *message = l->operator [](i);

        if(message->dest_session_id == dest_session_id &&
           message->dest_message_id == dest_message_id) {
          message->src_session_id = src_session_id;

          QString db_postfix(
            QDateTime::fromMSecsSinceEpoch(message->receive_date)
            .toString("dd_MM_yyyy"));

          if(!i_messages.contains(db_postfix)) {
            i_messages.insert(db_postfix, QString());
          }

          QByteArray submit_text;

          if((esm_class & 0x40) == 0) {
            submit_text = short_message.mid(0, 20);
          } else {
            if(short_message.size() > 7) {
              submit_text = short_message.mid(6, 20);
            }
          }

          QString stat;

          switch(message->message_state) {
            case smpp::parameters::message_state::ACCEPTED:
              stat = "ACCEPTD";
              break;
            case smpp::parameters::message_state::DELIVERED:
              stat = "DELIVRD";
              break;
            case smpp::parameters::message_state::EXPIRED:
              stat = "EXPIRED";
              break;
            case smpp::parameters::message_state::DELETED:
              stat = "DELETED";
              break;
            case smpp::parameters::message_state::UNDELIVERABLE:
              stat = "UNDELIV";
              break;
            case smpp::parameters::message_state::REJECTED:
              stat = "REJECTD";
              break;
            default:
              stat = "UNKNOWN";
              break;
          }

          QByteArray deliver_text = smpp::helpers::toDataCodes(
            "id:" + QString().sprintf("%.10d", src_message_id).toUtf8() +
            " sub:001 dlvrd:001 submit date:" +
            QDateTime::fromMSecsSinceEpoch(receive_date)
              .toString("yyMMddHHmm").toUtf8() +
            " done date:" + QDateTime::fromMSecsSinceEpoch(now_date)
              .toString("yyMMddHHmm").toUtf8() +
            " stat:" + stat.toUtf8() +
            " err:" + QString().sprintf("%03x", message->error_code).toUtf8() +
            " text:" + submit_text, message->dm.getDataCoding());

          if(message->message_state !=
             smpp::parameters::message_state::DELIVERED) {
            Kernel::instance()->altRoute(message);
          }

          if(message->alt_dest_session_id == 0) {
            if(registered_delivery ==
                smpp::parameters::registered_delivery::NO_DELIVERY) {
              if(!d_directions.isEmpty()) {
                d_directions += ',';
              }

              d_directions += QString::number(src_message_id);
            } else {
              if(!u_directions->next_delivery_date.isEmpty()) {
                u_directions->receive_date += " ";
                u_directions->next_delivery_date += " ";
                u_directions->error_code += " ";
                u_directions->message_state += " ";
                u_directions->src_addr_ton_before += " ";
                u_directions->src_addr_npi_before += " ";
                u_directions->src_addr_before += " ";
                u_directions->src_addr_ton_after += " ";
                u_directions->src_addr_npi_after += " ";
                u_directions->src_addr_after += " ";
                u_directions->dest_addr_ton_before += " ";
                u_directions->dest_addr_npi_before += " ";
                u_directions->dest_addr_before += " ";
                u_directions->dest_addr_ton_after += " ";
                u_directions->dest_addr_npi_after += " ";
                u_directions->dest_addr_after += " ";
                u_directions->sm_length += " ";
                u_directions->short_message += " ";
                u_directions->where += ",";
              }

              u_directions->receive_date += QString("WHEN %1 THEN %2")
               .arg(src_message_id)
               .arg(message->receive_date);
              u_directions->next_delivery_date += QString("WHEN %1 THEN %2")
               .arg(src_message_id)
               .arg(message->receive_date);
              u_directions->error_code += QString("WHEN %1 THEN %2")
                .arg(src_message_id)
                .arg(smpp::parameters::command_status::ESME_ROK);
              u_directions->message_state += QString("WHEN %1 THEN %2")
                .arg(src_message_id)
                .arg(smpp::parameters::message_state::ENROUTE);
              u_directions->src_addr_ton_before += QString("WHEN %1 THEN %2")
                .arg(src_message_id)
                .arg(dest_addr_ton_after);
              u_directions->src_addr_npi_before += QString("WHEN %1 THEN %2")
                .arg(src_message_id)
                .arg(dest_addr_npi_after);
              u_directions->src_addr_before += QString("WHEN %1 THEN '%2'")
                .arg(src_message_id)
                .arg(dest_addr_after);
              u_directions->src_addr_ton_after += QString("WHEN %1 THEN %2")
                .arg(src_message_id)
                .arg(dest_addr_ton_before);
              u_directions->src_addr_npi_after += QString("WHEN %1 THEN %2")
                .arg(src_message_id)
                .arg(dest_addr_npi_before);
              u_directions->src_addr_after += QString("WHEN %1 THEN '%2'")
                .arg(src_message_id)
                .arg(dest_addr_before);
              u_directions->dest_addr_ton_before += QString("WHEN %1 THEN %2")
                .arg(src_message_id)
                .arg(src_addr_ton_after);
              u_directions->dest_addr_npi_before += QString("WHEN %1 THEN %2")
                .arg(src_message_id)
                .arg(src_addr_npi_after);
              u_directions->dest_addr_before += QString("WHEN %1 THEN '%2'")
                .arg(src_message_id)
                .arg(src_addr_after);
              u_directions->dest_addr_ton_after += QString("WHEN %1 THEN %2")
                .arg(src_message_id)
                .arg(src_addr_ton_before);
              u_directions->dest_addr_npi_after += QString("WHEN %1 THEN %2")
                .arg(src_message_id)
                .arg(src_addr_npi_before);
              u_directions->dest_addr_after += QString("WHEN %1 THEN '%2'")
                .arg(src_message_id)
                .arg(src_addr_before);
              u_directions->sm_length += QString("WHEN %1 THEN %2")
                .arg(src_message_id)
                .arg(deliver_text.size());
              u_directions->short_message += QString("WHEN %1 THEN '%2'")
                .arg(src_message_id)
                .arg(QString(deliver_text.toHex()));
              u_directions->where += QString::number(src_message_id);
            }
          } else {
            if(!d_directions.isEmpty()) {
              d_directions += ',';
            }

            d_directions += QString::number(src_message_id);

            smpp::pdu::SubmitSm sm(0,
                                   service_type,
                                   src_addr_ton_before,
                                   src_addr_npi_before,
                                   src_addr_before,
                                   dest_addr_ton_before,
                                   dest_addr_npi_before,
                                   dest_addr_before,
                                   esm_class,
                                   protocol_id,
                                   priority_flag,
                                   schedule_delivery_time,
                                   validity_period,
                                   registered_delivery,
                                   replace_if_present_flag,
                                   data_coding,
                                   sm_default_msg_id,
                                   short_message.size(),
                                   short_message);

            sm.setCommandLength(sm.size());

            SrcSubmitSm *submit_message(
              new SrcSubmitSm(src_session_id, src_message_id, sm));

            submit_message->dest_session_id = message->alt_dest_session_id;

            emit parent->send(submit_message);
          }

          if(!i_messages[db_postfix].isEmpty()) {
            i_messages[db_postfix] += ",";
          }

          i_messages[db_postfix] += QString(
            "(%1,2,%2,%3,%4,%5,%6,%7,'%8',%9,%10,'%11',%12,%13,'%14',%15,%16,"
            "'%17',%18,%19,'%20',%21,%22,%23,'%24','%25',%26,%27,%28,%29,%30,"
            "'%31',%32,%33)")
            .arg(Kernel::instance()->getKernelId())
            .arg(src_session_id)
            .arg(message->dest_session_id)
            .arg(src_message_id)
            .arg(dest_message_id)
            .arg(message->receive_date)
            .arg(message->receive_date)
            .arg("")
            .arg(dest_addr_ton_after)
            .arg(dest_addr_npi_after)
            .arg(dest_addr_after)
            .arg(dest_addr_ton_before)
            .arg(dest_addr_npi_before)
            .arg(dest_addr_before)
            .arg(src_addr_ton_after)
            .arg(src_addr_npi_after)
            .arg(src_addr_after)
            .arg(src_addr_ton_before)
            .arg(src_addr_npi_before)
            .arg(src_addr_before)
            .arg(0x04)
            .arg(0)
            .arg(0)
            .arg("")
            .arg("")
            .arg(0)
            .arg(0)
            .arg(0)
            .arg(0)
            .arg(deliver_text.size())
            .arg(QString(deliver_text.toHex()))
            .arg(message->error_code)
            .arg(message->message_state);
          delete message;
          l->takeAt(i);
          break;
        } else {
          i++;
        }
      }
    }
  } else if(Kernel::instance()->getLogLevel() >= smslog::WARNING) {
    QMultiHash<int, QVariant> params;
    params.insert(logparam::ACTION_P, QString("writeDestDeliver"));
    params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
    params.insert(logparam::ERROR_MSG_P, q->getFaultString());

    LogEncoder::instance()->encode(smslog::WARNING,
                                   logclass::MAILER,
                                   logclass::MAILER,
                                   logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                   params);
  }

  if(l) {
    qDeleteAll(l->begin(), l->end());
    l->clear();
    delete l;
    l = 0;
  }

  dbmanager::SqlQuery *directions_query = new dbmanager::SqlQuery(false, true);
  dbmanager::SqlQuery *messages_query = new dbmanager::SqlQuery(false, true);

  queries.push_back(directions_query);
  queries.push_back(messages_query);

  connect(directions_query, SIGNAL(executed()),
          this, SLOT(writeDestDeliverDirectionsExecuted()));

  connect(messages_query, SIGNAL(executed()),
          this, SLOT(writeDestDeliverMessagesExecuted()));

  if(!d_directions.isEmpty()) {
    directions_query->add(QString(
      "DELETE FROM `directions` WHERE `state`='R_SBT_RESP_DST' AND "
      "`kernel_id`=%1 AND `src_message_id` IN (%2)")
        .arg(Kernel::instance()->getKernelId())
        .arg(d_directions));
  }

  if(!u_directions->next_delivery_date.isEmpty()) {
    directions_query->add(QString(
      "UPDATE `directions` "
      "SET `state` = 'R_DLV_DST',"
      "`receive_date` = CASE `src_message_id` %1 END,"
      "`attempt` = 0,"
      "`next_delivery_date` = CASE `src_message_id` %2 END,"
      "`error_code` = CASE `src_message_id` %3 END,"
      "`message_state` = CASE `src_message_id` %4 END,"
      "`src_addr_ton_before` = CASE `src_message_id` %5 END,"
      "`src_addr_npi_before` = CASE `src_message_id` %6 END,"
      "`src_addr_before` = CASE `src_message_id` %7 END,"
      "`src_addr_ton_after` = CASE `src_message_id` %8 END,"
      "`src_addr_npi_after` = CASE `src_message_id` %9 END,"
      "`src_addr_after` = CASE `src_message_id` %10 END,"
      "`dest_addr_ton_before` = CASE `src_message_id` %11 END,"
      "`dest_addr_npi_before` = CASE `src_message_id` %12 END,"
      "`dest_addr_before` = CASE `src_message_id` %13 END,"
      "`dest_addr_ton_after` = CASE `src_message_id` %14 END,"
      "`dest_addr_npi_after` = CASE `src_message_id` %15 END,"
      "`dest_addr_after` = CASE `src_message_id` %16 END,"
      "`sm_length` = CASE `src_message_id` %17 END,"
      "`short_message` = CASE `src_message_id` %18 END "
      "WHERE `state`='R_SBT_RESP_DST' AND `kernel_id`=%19 AND "
      "`src_message_id` IN (%20)")
        .arg(u_directions->receive_date)
        .arg(u_directions->next_delivery_date)
        .arg(u_directions->error_code)
        .arg(u_directions->message_state)
        .arg(u_directions->src_addr_ton_before)
        .arg(u_directions->src_addr_npi_before)
        .arg(u_directions->src_addr_before)
        .arg(u_directions->src_addr_ton_after)
        .arg(u_directions->src_addr_npi_after)
        .arg(u_directions->src_addr_after)
        .arg(u_directions->dest_addr_ton_before)
        .arg(u_directions->dest_addr_npi_before)
        .arg(u_directions->dest_addr_before)
        .arg(u_directions->dest_addr_ton_after)
        .arg(u_directions->dest_addr_npi_after)
        .arg(u_directions->dest_addr_after)
        .arg(u_directions->sm_length)
        .arg(u_directions->short_message)
        .arg(Kernel::instance()->getKernelId())
        .arg(u_directions->where));
  }

  for(QHash<QString, QString>::const_iterator i =
        i_messages.constBegin();
      i != i_messages.constEnd();
      i++)
  if(!i_messages.isEmpty()) {
    messages_query->add(QString(
      "INSERT INTO `messages_%1` ("
      "`kernel_id`,"
      "`message_type`,"
      "`src_session_id`,"
      "`dest_session_id`,"
      "`src_message_id`,"
      "`dest_message_id`,"
      "`receive_date`,"
      "`next_delivery_date`,"
      "`service_type`,"
      "`src_addr_ton_before`,"
      "`src_addr_npi_before`,"
      "`src_addr_before`,"
      "`src_addr_ton_after`,"
      "`src_addr_npi_after`,"
      "`src_addr_after`,"
      "`dest_addr_ton_before`,"
      "`dest_addr_npi_before`,"
      "`dest_addr_before`,"
      "`dest_addr_ton_after`,"
      "`dest_addr_npi_after`,"
      "`dest_addr_after`,"
      "`esm_class`,"
      "`protocol_id`,"
      "`priority_flag`,"
      "`schedule_delivery_time`,"
      "`validity_period`,"
      "`registered_delivery`,"
      "`replace_if_present_flag`,"
      "`data_coding`,"
      "`sm_default_msg_id`,"
      "`sm_length`,"
      "`short_message`,"
      "`error_code_smsc`,"
      "`message_state_smsc`) VALUES %2")
      .arg(i.key())
      .arg(i.value()));
  }

  delete u_directions;

  Kernel::instance()->dbDirections()->execute(directions_query);
  Kernel::instance()->dbMessages()->execute(messages_query);

  delete q;
  checkState();
}

void MailerPrivate::writeDestDeliverDirectionsExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);

  if(!q->success() && Kernel::instance()->getLogLevel() >= smslog::WARNING) {
    QMultiHash<int, QVariant> params;
    params.insert(logparam::ACTION_P, QString("writeDestDeliverDirections"));
    params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
    params.insert(logparam::ERROR_MSG_P, q->getFaultString());

    LogEncoder::instance()->encode(smslog::WARNING,
                                   logclass::MAILER,
                                   logclass::MAILER,
                                   logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                   params);
  }

  delete q;
  checkState();
}

void MailerPrivate::writeDestDeliverMessagesExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);

  if(!q->success() && Kernel::instance()->getLogLevel() >= smslog::WARNING) {
    QMultiHash<int, QVariant> params;
    params.insert(logparam::ACTION_P, QString("writeDestDeliverMessages"));
    params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
    params.insert(logparam::ERROR_MSG_P, q->getFaultString());

    LogEncoder::instance()->encode(smslog::WARNING,
                                   logclass::MAILER,
                                   logclass::MAILER,
                                   logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                   params);
  }

  delete q;
  checkState();
}
void MailerPrivate::readDestSubmitExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);

  if(q->success()) {
    QString list_id;
    QList<QSqlRecord *> res(q->results());
    for(int i = 0; i < res.size(); i++) {
      if(!list_id.isEmpty())
        list_id += ',';
      list_id += QString::number(res[i]->value(3).toUInt());
    }
    if(!list_id.isEmpty()) {
      dbmanager::SqlQuery *query = new dbmanager::SqlQuery(false, true);

      query->add(
        QString("UPDATE `directions` SET `state`='T_SBT_DST' "
                "WHERE `state`='R_SBT_SRC' AND `src_message_id` IN (%1) AND "
                "`kernel_id`=%2")
          .arg(list_id)
          .arg(Kernel::instance()->getKernelId()));

      connect(query, SIGNAL(executed()),
              this, SLOT(updateDestSubmitExecuted()));

      queries.push_back(query);

      return Kernel::instance()->dbDirections()->execute(query);
    }
  } else {
    if(!q->success() && Kernel::instance()->getLogLevel() >= smslog::WARNING) {
      QMultiHash<int, QVariant> params;
      params.insert(logparam::ACTION_P, QString("readDestSubmit"));
      params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
      params.insert(logparam::ERROR_MSG_P, q->getFaultString());

      LogEncoder::instance()->encode(smslog::WARNING,
                                     logclass::MAILER,
                                     logclass::MAILER,
                                     logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                     params);
    }
  }

  rd_dest_submit_query = 0;
  delete q;
  checkState();
}

void MailerPrivate::readSrcDeliverExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);

  if(q->success()) {
    QString list_id;
    QList<QSqlRecord *> res(q->results());
    for(int i = 0; i < res.size(); i++) {
      if(!list_id.isEmpty())
        list_id += ',';
      list_id += QString::number(res[i]->value(2).toUInt());
    }
    if(!list_id.isEmpty()) {
      dbmanager::SqlQuery *query = new dbmanager::SqlQuery(false, true);

      query->add(
        QString("UPDATE `directions` SET `state`='T_DLV_SRC' "
                "WHERE `state`='R_DLV_DST' AND `src_message_id` IN (%1) AND "
                "`kernel_id`=%2")
          .arg(list_id)
          .arg(Kernel::instance()->getKernelId()));

      connect(query, SIGNAL(executed()),
              this, SLOT(updateSrcDeliverExecuted()));

      queries.push_back(query);

      return Kernel::instance()->dbDirections()->execute(query);
    }
  } else {
    if(!q->success() && Kernel::instance()->getLogLevel() >= smslog::WARNING) {
      QMultiHash<int, QVariant> params;
      params.insert(logparam::ACTION_P, QString("readSrcDeliver"));
      params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
      params.insert(logparam::ERROR_MSG_P, q->getFaultString());

      LogEncoder::instance()->encode(smslog::WARNING,
                                     logclass::MAILER,
                                     logclass::MAILER,
                                     logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                     params);
    }
  }

  rd_src_deliver_query = 0;
  delete q;
  checkState();
}

void MailerPrivate::updateDestSubmitExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);

  if(q->success()) {
    if(rd_dest_submit_query) {
      QList<QSqlRecord *> res(rd_dest_submit_query->results());
      while(!res.isEmpty()) {
        QSqlRecord *r(res.takeFirst());

        quint32 dest_session_id(r->value(1).toUInt());

        smpp::pdu::SubmitSm sm(
          0,
          r->value(5).toString(),
          r->value(6).toUInt(),
          r->value(7).toUInt(),
          r->value(8).toString(),
          r->value(9).toUInt(),
          r->value(10).toUInt(),
          r->value(11).toString(),
          r->value(12).toUInt(),
          r->value(13).toUInt(),
          r->value(14).toUInt(),
          r->value(15).toString(),
          r->value(16).toString(),
          smpp::parameters::registered_delivery::DELIVERY_ALL,
          r->value(18).toUInt(),
          r->value(19).toUInt(),
          r->value(20).toUInt(),
          r->value(21).toUInt(),
          QByteArray::fromHex(r->value(22).toByteArray()));

        sm.setCommandLength(sm.size());

        DestSubmitSm *message(new DestSubmitSm(r->value(0).toUInt(),
                                               dest_session_id,
                                               r->value(2).toULongLong(),
                                               r->value(3).toUInt(),
                                               r->value(23).toUInt(),
                                               r->value(24).toUInt(),
                                               r->value(25).toString(),
                                               sm,
                                               r->value(4).toUInt() + 1));

        rd_dest_submit_messages.push_back(message);

        emit parent->send(message);
      }

      delete rd_dest_submit_query;
      rd_dest_submit_query = 0;
    }
  } else if(Kernel::instance()->getLogLevel() >= smslog::WARNING) {
    if(rd_dest_submit_query) {
      delete rd_dest_submit_query;
      rd_dest_submit_query = 0;
    }
    QMultiHash<int, QVariant> params;
    params.insert(logparam::ACTION_P, QString("updateDestSubmit"));
    params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
    params.insert(logparam::ERROR_MSG_P, q->getFaultString());

    LogEncoder::instance()->encode(smslog::WARNING,
                                   logclass::MAILER,
                                   logclass::MAILER,
                                   logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                   params);
  }

  delete q;

  checkState();
}

void MailerPrivate::updateSrcDeliverExecuted() {
  dbmanager::SqlQuery *q(qobject_cast<dbmanager::SqlQuery *>(sender()));

  if(!q) {
    return;
  }

  queries.removeAll(q);

  if(q->success()) {
    if(rd_src_deliver_query) {
      QList<QSqlRecord *> res(rd_src_deliver_query->results());
      while(!res.isEmpty()) {
        QSqlRecord *r(res.takeFirst());

        quint32 src_session_id(r->value(0).toUInt());

        smpp::pdu::DeliverSm dm(
          0,
          "",//r->value(4).toString(),
          r->value(5).toUInt(),
          r->value(6).toUInt(),
          r->value(7).toString(),
          r->value(8).toUInt(),
          r->value(9).toUInt(),
          r->value(10).toString(),
          0x04,//r->value(11).toUInt(),
          0,//r->value(12).toUInt(),
          0,//r->value(13).toUInt(),
          "",//r->value(14).toString(),
          "",//r->value(15).toString(),
          0,//r->value(16).toUInt(),
          0,//r->value(17).toUInt(),
          0,//r->value(18).toUInt(),
          0,//r->value(19).toUInt(),
          r->value(20).toUInt(),
          QByteArray::fromHex(r->value(21).toByteArray()));

        dm.setCommandLength(dm.size());

        SrcDeliverSm *message(new SrcDeliverSm(src_session_id,
                                               r->value(1).toULongLong(),
                                               r->value(2).toUInt(),
                                               dm,
                                               r->value(3).toUInt() + 1));

        rd_src_deliver_messages.push_back(message);

        emit parent->send(message);
      }

      delete rd_src_deliver_query;
      rd_src_deliver_query = 0;
    }
  } else if(Kernel::instance()->getLogLevel() >= smslog::WARNING) {
    if(rd_src_deliver_query) {
      delete rd_src_deliver_query;
      rd_src_deliver_query = 0;
    }
    QMultiHash<int, QVariant> params;
    params.insert(logparam::ACTION_P, QString("updateSrcDeliver"));
    params.insert(logparam::ERROR_CODE_P, q->getFaultCode());
    params.insert(logparam::ERROR_MSG_P, q->getFaultString());

    LogEncoder::instance()->encode(smslog::WARNING,
                                   logclass::MAILER,
                                   logclass::MAILER,
                                   logmsgcodes::ERROR_EXEC_SQL_QUERY,
                                   params);
  }

  delete q;

  checkState();
}

void MailerPrivate::setState(State state) {
  if(this->state == state)
    return;

  switch(state) {
    case ERROR:
      if(state == STOPING) {
        return;
      } else if(Kernel::instance()->getLogLevel() >= smslog::INFO) {
        LogEncoder::instance()->encode(smslog::INFO,
                                       logclass::MAILER,
                                       logclass::MAILER,
                                       logmsgcodes::M_ERROR_STATE);
      }
      break;
    case STOPING:
      if(state == STOPED) {
        return;
      } else if(Kernel::instance()->getLogLevel() >= smslog::INFO) {
        LogEncoder::instance()->encode(smslog::INFO,
                                       logclass::MAILER,
                                       logclass::MAILER,
                                       logmsgcodes::M_STOPING_STATE);
      }
      break;
    case STARTING:
      if(state == STARTED) {
        return;
      } else {
        if(Kernel::instance()->getLogLevel() >= smslog::INFO) {
          LogEncoder::instance()->encode(smslog::INFO,
                                         logclass::MAILER,
                                         logclass::MAILER,
                                         logmsgcodes::M_STARTING_STATE);
        }
      }
      break;
    case STARTED:
      if(proc_timer == -1) {
        proc_timer = startTimer(proc_time);
      }
      if(Kernel::instance()->getLogLevel() >= smslog::INFO) {
        LogEncoder::instance()->encode(smslog::INFO,
                                       logclass::MAILER,
                                       logclass::MAILER,
                                       logmsgcodes::M_STARTED_STATE);
      }
      break;
    case STOPED:
      if(proc_timer != -1) {
        killTimer(proc_timer);
        proc_timer = -1;
      }
      if(Kernel::instance()->getLogLevel() >= smslog::INFO) {
        LogEncoder::instance()->encode(smslog::INFO,
                                       logclass::MAILER,
                                       logclass::MAILER,
                                       logmsgcodes::M_STOPED_STATE);
      }
      break;
  }

  this->state = state;

  emit parent->stateChanged(state);
}

void MailerPrivate::checkState() {
  switch(state) {
    case STOPING:
      if(queries.isEmpty() &&
         wr_src_submit_messages.isEmpty() &&
         wr_src_deliver_messages.isEmpty() &&
         wr_dest_submit_messages.isEmpty() &&
         wr_dest_deliver_messages.isEmpty() &&
         rd_src_deliver_messages.isEmpty() &&
         rd_dest_submit_messages.isEmpty()) {
        setState(STOPED);
      }
      break;
    default:
      break;
  }
}

Mailer::Mailer() : QObject(0), p(new MailerPrivate(this)) {

}

Mailer::~Mailer() {
  if(p) {
    if(p->thread) {
      if(p->thread->isRunning()) {
        p->thread->quit();
        p->thread->wait();
      }
    }
    delete p;
    p = 0;
  }
}

State Mailer::getState() {
  return p->state;
}

void Mailer::start() {
  p->thread->start();
}

void Mailer::stop() {
  emit p->sigStop();
}

void Mailer::add(SrcSubmitSm *message) {
  emit p->sigAdd(message);
}

void Mailer::add(DestSubmitSm *message) {
  emit p->sigAdd(message);
}

void Mailer::add(DestDeliverSm *message) {
  emit p->sigAdd(message);
}

void Mailer::add(SrcDeliverSm *message) {
  emit p->sigAdd(message);
}

} // namespace smsrouter
